package edu.zju.gis.dldsj.controller;

import edu.zju.gis.dldsj.base.BaseMapper;
import edu.zju.gis.dldsj.common.Result;
import edu.zju.gis.dldsj.config.CommonSetting;
import edu.zju.gis.dldsj.config.DataConfig;
import edu.zju.gis.dldsj.constant.CodeConstants;
import edu.zju.gis.dldsj.entity.DataField;
import edu.zju.gis.dldsj.entity.vo.StorageWithVisit;
import edu.zju.gis.dldsj.model.Jsonable;
import edu.zju.gis.dldsj.model.Storage;
import edu.zju.gis.dldsj.service.StorageFieldService;
import edu.zju.gis.dldsj.service.StorageService;
import edu.zju.gis.dldsj.utils.ElasticSearchHelper;
import edu.zju.gis.dldsj.utils.GeometryUtil;
import edu.zju.gis.dldsj.utils.HBaseHelper;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.io.Serializable;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.ExecutionException;

/**
 * @author yanlo yanlong_lee@qq.com
 * @version 1.0 2018/08/09
 */
@Slf4j
@CrossOrigin
@Controller
@RequestMapping("/data")
public class DataController {
    @Autowired
    private CommonSetting setting;
    @Autowired
    private DataConfig dataConfig;
    @Autowired
    private StorageService storageService;
    @Autowired
    private StorageFieldService fieldService;
    private ElasticSearchHelper esHelper;

    @RequestMapping(value = "/list", method = RequestMethod.GET)
    @ResponseBody
    public String getDataList(Integer offset, int size, String type) {
        Result<Map<String, Object>> result = new Result<>();
        List<StorageWithVisit> dataWithVisits;
        int totalSize;
        if (type == null || type.isEmpty()) {
            dataWithVisits = storageService.getByVisitDesc(offset == null ? 0 : offset, size);
            totalSize = storageService.getTotalSize();
        } else {
            dataWithVisits = storageService.getByCommentAndVisitDesc(type, offset == null ? 0 : offset, size);
            totalSize = storageService.getSizeByComment(type);
        }
        Map<String, Object> body = new HashMap<>();
        body.put("data", dataWithVisits);
        body.put("totalSize", totalSize);
        result.setCode(CodeConstants.SUCCESS).setBody(body);
        return result.toString();
    }

    @RequestMapping(value = "/field/{dataId}", method = RequestMethod.GET)
    @ResponseBody
    public String getFieldInfo(@PathVariable String dataId) {
        Result<List<DataField>> result = new Result<>();
        List<DataField> fields = fieldService.getByDataId(dataId);
        result.setCode(CodeConstants.SUCCESS).setBody(fields);
        return result.toString();
    }

    /**
     * 以json的格式读取指定数据的部分记录
     *
     * @param dataId 数据ID
     * @param offset 读取偏移量
     * @param size   读取的最大记录数
     */
    @RequestMapping(value = "/preview/{dataId}", method = RequestMethod.GET)
    @ResponseBody
    public String preview(@PathVariable String dataId, Integer offset, int size) {
        Result<List<? extends Jsonable>> result = new Result<>();
        try {
            Storage storage = storageService.select(dataId);
            if (storage == null) {
                result.setCode(CodeConstants.SERVICE_ERROR).setMessage("找不到ID为`" + dataId + "`的数据");
            } else {
                List<? extends Jsonable> first = getData(storage, offset == null ? 0 : offset, size);
                result.setCode(CodeConstants.SUCCESS).setBody(first);
            }
        } catch (Exception e) {
            log.error("数据读取失败", e);
            result.setCode(CodeConstants.SERVICE_ERROR).setMessage("数据读取失败：" + e.getMessage());
        }
        return result.toString();
    }

    /**
     * 以geojson的格式读取指定数据的部分记录
     *
     * @param dataId 数据ID
     * @param offset 读取偏移量
     * @param size   读取的最大记录数
     */
    @RequestMapping(value = "/preview/{dataId}/geojson")
    @ResponseBody
    public String getAsGeoJSON(@PathVariable String dataId, Integer offset, int size) {
        Result<JSONObject> result = new Result<>();
        try {
            Storage storage = storageService.select(dataId);
            if (storage == null) {
                result.setCode(CodeConstants.SERVICE_ERROR).setMessage("找不到ID为`" + dataId + "`的数据");
            } else {
                List<? extends Jsonable> records = getData(storage, offset == null ? 0 : offset, size);
                if (records.isEmpty()) {
                    result.setCode(CodeConstants.SERVICE_ERROR).setMessage("该数据为空");
                } else {
                    JSONObject geojson = GeometryUtil.toGeoJSON(records, storage.getMeta_name());
                    result.setCode(CodeConstants.SUCCESS).setBody(geojson);
                }
            }
        } catch (Exception e) {
            log.error("数据读取为geojson异常", e);
            result.setCode(CodeConstants.SERVICE_ERROR).setMessage("数据读取失败" + e.getMessage());
        }
        return result.toString();
    }

    /**
     * 根据数据ID、起始位置和结果的数量读取数据
     *
     * @param offset 起始读取位置
     * @param size   读取记录的最大数量
     */
    public List<? extends Jsonable> getData(Storage storage, int offset, int size)
            throws IOException, ReflectiveOperationException, ExecutionException, InterruptedException {
        List<? extends Jsonable> first = Collections.emptyList();
        String storageType = storage.getStorage_type();
        List<String> types = Arrays.asList(storageType.split(","));
        List<String> locations = Arrays.asList(storage.getStorage_location().split(","));
        if (storageType.contains("ELASTICSEARCH")) {
            if (esHelper == null)
                try {
                    esHelper = new ElasticSearchHelper(setting.getEsHost(), setting.getEsPort(), setting.getEsName(), setting.getEsIndex());
                } catch (UnknownHostException e) {
                    log.error("ElasticSearchHelper初始化异常", e);
                    throw e;
                }
            String location = locations.get(types.indexOf("ELASTICSEARCH"));
            first = esHelper.getAsModel(dataConfig.getModel(location), location, offset, size);
        } else if (storageType.contains("MYSQL")) {
            String location = locations.get(types.indexOf("MYSQL"));
            BaseMapper<? extends Jsonable, ? extends Serializable> mapper = dataConfig.getMapper(location);
            first = mapper.selectByPage(offset, size);
        } else if (storageType.contains("HBASE")) {
            String location = locations.get(types.indexOf("HBASE"));
            HBaseHelper hbaseHelper = HBaseHelper.getInstance();
            if (!hbaseHelper.exists(location)) {
                first = Collections.emptyList();
            } else {
                org.apache.hadoop.hbase.client.Result[] results = hbaseHelper.getScanner(location, offset, size).next(size);
                first = HBaseHelper.resultToModel(dataConfig.getModel(location), results);
            }
        }
        return first;
    }

}
